package com.flexfiltering.view.collections
{
import com.flexfiltering.core.flexfiltering_internal;

import flash.utils.clearTimeout;
import flash.utils.getTimer;
import flash.utils.setTimeout;

import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;

use namespace flexfiltering_internal;

public class FilteredCollectionView extends ArrayCollection
{
	
	// @------------------------------------------------------ STATIC PROPERTIES
	
	public static const MATCH_CONTAINS:String = "";
	public static const MATCH_BEGINS_WITH:String = "* ";
	public static const MATCH_ANY_WORD_BEGINS_WITH:String = " "
	
	public static const MODE_SHOW_ALL:int = 0;
	public static const MODE_SHOW_NONE:int = 1;
	
	public static var refreshDelay:int = 10;
	
	// @-------------------------------------------------------PUBLIC PROPERTIES
	
	public var mode:int;
	
	//----------------------------------
	//  caseSensitive
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the caseSensitive property.
	 */
	private var _caseSensitive:Boolean;

	[Bindable()]
	/**
	 *  
	 * 
	 * 	@default 
	 */ 
	public function get caseSensitive() : Boolean
	{
	    return _caseSensitive;
	}

	/**
	 *  @private
	 */
	public function set caseSensitive ( val:Boolean ) : void
	{
		_caseSensitive = val;
		generateIndex();	
	}
	
	//----------------------------------
	//  filterField
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the filterField property.
	 */
	private var _filterField:String;

	[Bindable()]
	/**
	 *  
	 * 
	 * 	@default 
	 */ 
	public function get filterField() : String
	{
	    return _filterField;
	}

	/**
	 *  @private
	 */
	public function set filterField ( val:String ) : void
	{
		_filterField = val;
		generateIndex();
	}
	
	//----------------------------------
	//  filterSubstring
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the filterSubstring property.
	 */
	private var _filterSubstring:String;

	[Bindable()]
	/**
	 *  
	 * 
	 * 	@default 
	 */ 
	public function get filterSubstring() : String
	{
	    return _filterSubstring;
	}

	/**
	 *  @private
	 */
	public function set filterSubstring ( val:String ) : void
	{
		if ( !caseSensitive ) _filterSubstring = val.toLowerCase();
		else _filterSubstring = val;
		
		if ( refreshTimeout ) clearTimeout(refreshTimeout);
		refreshTimeout = setTimeout(refresh,refreshDelay);
	}
	
	//----------------------------------
	//  matchType
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the matchType property.
	 */
	private var _matchType:String;

	[Bindable()]
	/**
	 *  
	 * 
	 * 	@default 
	 */ 
	public function get matchType() : String
	{
	    return _matchType;
	}

	/**
	 *  @private
	 */
	public function set matchType ( val:String ) : void
	{
		_matchType = val;
		refresh();
	}
	
	// @---------------------------------------------------- INTERNAL PROPERTIES
	
	flexfiltering_internal var index:String;
	protected var refreshTimeout:int;
	protected var initalizing:Boolean
	
	// @-------------------------------------------------- OVERRIDDEN PROPERTIES
	// @------------------------------------------------------------ CONSTRUCTOR
	
	public function FilteredCollectionView( source:Array=null,
											filterField:String=null,
											caseSensitive:Boolean=false,
											matchType:String=MATCH_BEGINS_WITH,
											index:String="" )
	{
		_filterField = filterField;
		_caseSensitive = caseSensitive;
		_matchType = matchType;
		if ( index )
		{
			this.index = index;
			initalizing = true;
		}
		super(source);
		initalizing = false;
	}
	
	// @--------------------------------------------------------- PUBLIC METHODS
	
	//----------------------------------
	//  clone
	//----------------------------------
	
	/**
	 * 	Cloning a FilteredViewCollection is useful because it will create
	 * 	another view of the same source data, that can be filtered differently
	 * 	but it will not incur the overhead of generating a new index string
	 */
	
	public function clone () : FilteredCollectionView
	{
		return new FilteredCollectionView ( source, 
											filterField,
											caseSensitive,
											matchType,
											index );
	} 
	
	// @------------------------------------------------------- INTERNAL METHODS
	
	//----------------------------------
	//  generateIndex
	//----------------------------------
	
	protected function generateIndex () : void
	{
		index = "";
			
		var start:int = getTimer();
		var len:int = source.length;
		
		for ( var i:int=0; i<len; i++ )
		{
			var key:String = "";
			
			if ( filterField )
			{
				key = source[i][filterField];	
			}
			else
			{
				key = source[i].toString();	
			}
			
			index += "* "+key+"<"+i+">";
		}
		
		if ( !caseSensitive )
			index = index.toLowerCase();
		
		trace("indexed "+len+" items in "+(getTimer()-start)+"ms");
	}
	
	// @----------------------------------------------------- OVERRIDDEN METHODS
	
	//----------------------------------
	//  source
	//----------------------------------
	
	override public function set source(s:Array):void
	{
		super.source = s;
		
		if ( s && !initalizing ) generateIndex();
	}
	
	//----------------------------------
	//  refresh
	//----------------------------------
	
	override public function refresh():Boolean
	{
        super.refresh();
		
		if ( !filterSubstring || filterSubstring == "" )
		{
			return true;
		}
		
		var tmp:Array = [];
		var i:int = 0;
		var pos:int = 0;
		var idx:int = 0;
		var count:int = 0;
		
		while(i > -1){
		
			pos = index.indexOf(_matchType+filterSubstring, i);
		
			if(pos > -1)
			{
				idx = int( index.substring(index.indexOf("<", pos)+1, index.indexOf(">", pos)) );
				
				tmp.push(source[idx]);
				count++;
				
				//fix : Bug # 1628014: once a match was found this index was not being advanced to the new row in the grid so multiple rows were being returned
				//in the case of the same term applying twice in a row.
				i = index.indexOf(">", pos);
			}
			else
			{
				i = pos;
			}
		}
		
		localIndex = tmp;
       	
       	if (sort)
        {
            sort.sort(localIndex);
        }
       	
       	var refreshEvent:CollectionEvent =
            new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
        refreshEvent.kind = CollectionEventKind.REFRESH;
        dispatchEvent(refreshEvent);
       	
       	return true;
	}
	
	// @--------------------------------------------------------- EVENT HANDLERS
	// @---------------------------------------------- OVERRIDDEN EVENT HANDLERS
	
	
	
}
}